/**
 * @file IxEthDataPlane.c
 *
 * @author Intel Corporation
 * @date 12-Feb-2002
 *
 * @brief This file contains the implementation of the IXPxxx 
 * Ethernet Access Data plane component
 *
 * Design Notes:
 *
 * @par
 * IXP400 SW Release version 2.1
 * 
 * -- Copyright Notice --
 * 
 * @par
 * Copyright (c) 2001-2005, Intel Corporation.
 * All rights reserved.
 * 
 * @par
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Intel Corporation nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * 
 * @par
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * 
 * @par
 * -- End of Copyright Notice --
 */

#include "IxNpeMh.h"
#include "IxEthAcc.h"
#include "IxEthDB.h"
#include "ixp_osal/IxOsal.h"
#include "IxEthDBPortDefs.h"
#include "IxFeatureCtrl.h"
#include "IxEthAcc_p.h"
#include "IxEthAccQueueAssign_p.h"

extern PUBLIC IxEthAccMacState ixEthAccMacState[];
extern PUBLIC UINT32 ixEthAccNewSrcMask;

/**
 * private functions prototype
 */
PRIVATE IX_OSAL_MBUF *
ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask);

PRIVATE UINT32 
ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf);

PRIVATE UINT32 
ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf);

PRIVATE IxEthAccStatus
ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId,
				IxEthAccTxPriority *priorityPtr);

PRIVATE IxEthAccStatus
ixEthAccTxFromSwQ(IxEthAccPortId portId,
		  IxEthAccTxPriority priority);

PRIVATE IxEthAccStatus
ixEthAccRxFreeFromSwQ(IxEthAccPortId portId);

PRIVATE void 
ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf);

PRIVATE void 
ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf);

PRIVATE IX_STATUS 
ixEthAccQmgrLockTxWrite(IxEthAccPortId portId, 
			UINT32 qBuffer);

PRIVATE IX_STATUS 
ixEthAccQmgrLockRxWrite(IxEthAccPortId portId, 
			UINT32 qBuffer);

PRIVATE IX_STATUS 
ixEthAccQmgrTxWrite(IxEthAccPortId portId, 
		    UINT32 qBuffer, 
		    UINT32 priority);

/**
 * @addtogroup IxEthAccPri
 *@{
 */

/* increment a counter only when stats are enabled */
#define TX_STATS_INC(port,field) \
        IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccTxData.stats.field)
#define RX_STATS_INC(port,field) \
        IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccRxData.stats.field)

/* always increment the counter (mainly used for unexpected errors) */
#define TX_INC(port,field) \
        ixEthAccPortData[port].ixEthAccTxData.stats.field++
#define RX_INC(port,field) \
        ixEthAccPortData[port].ixEthAccRxData.stats.field++

PRIVATE IxEthAccDataPlaneStats     ixEthAccDataStats;

extern IxEthAccPortDataInfo   ixEthAccPortData[];
extern IxEthAccInfo   ixEthAccDataInfo;

/* Lock used when writing to the RxFree queue for a port */
PRIVATE IxOsalFastMutex rxWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS];

/* Lock used when writing to the TX queue for a port */
PRIVATE IxOsalFastMutex txWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS];


/**
 *
 * @brief Mbuf header conversion macros : they implement the 
 *  different conversions using a temporary value. They also double-check
 *  that the parameters can be converted to/from NPE format. 
 *
 */
#if defined(__wince) && !defined(IN_KERNEL)
#define PTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (UINT32)IX_OSAL_MBUF_MBUF_VIRTUAL_TO_PHYSICAL_TRANSLATION((IX_OSAL_MBUF*)ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#define PTR_NPE2VIRT(type,src,ptrDst) \
  do { void *temp; \
      IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \
      (ptrDst) = (type)IX_OSAL_MBUF_MBUF_PHYSICAL_TO_VIRTUAL_TRANSLATION(temp); } \
  while(0)
#else
#define PTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#define PTR_NPE2VIRT(type,src,ptrDst) \
  do { void *temp; \
      IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \
      (ptrDst) = (type)IX_OSAL_MMU_PHYS_TO_VIRT(temp); } \
  while(0)
#endif

/**
 *
 * @brief Mbuf payload pointer conversion macros : Wince has its own 
 *  method to convert the buffer pointers
 */
#if defined(__wince) && !defined(IN_KERNEL)
#define DATAPTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      temp = (UINT32)IX_OSAL_MBUF_DATA_VIRTUAL_TO_PHYSICAL_TRANSLATION(ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#else
#define DATAPTR_VIRT2NPE(ptrSrc,dst) PTR_VIRT2NPE(IX_OSAL_MBUF_MDATA(ptrSrc),dst)
#endif


/* Flush the shared part of the mbuf header */
#define IX_ETHACC_NE_CACHE_FLUSH(mbufPtr) \
  do { \
      IX_OSAL_CACHE_FLUSH(IX_ETHACC_NE_SHARED(mbufPtr), \
			      sizeof(IxEthAccNe)); \
    } \
  while(0)

/* Invalidate the shared part of the mbuf header */
#define IX_ETHACC_NE_CACHE_INVALIDATE(mbufPtr) \
  do { \
      IX_OSAL_CACHE_INVALIDATE(IX_ETHACC_NE_SHARED(mbufPtr), \
				   sizeof(IxEthAccNe)); \
    } \
  while(0)

#ifdef _DIAB_TOOL
__asm volatile void _mbufPtrPrefetch (volatile void *mbufPtr) 
{
% reg mbufPtr;
	pld [mbufPtr];
}
#endif /* #ifdef _DIAB_TOOL */
  

/* Preload one cache line (shared mbuf headers are aligned
 * and their size is 1 cache line)
 *
 * IX_OSAL_CACHED  is defined when the mbuf headers are
 * allocated from cached memory.
 *
 * Other processor on emulation environment may not implement 
 * preload function
 */
#ifdef IX_OSAL_CACHED
	#if (CPU!=SIMSPARCSOLARIS) && !defined (__wince)
    	#ifdef _DIAB_TOOL
    		#define IX_ACC_DATA_CACHE_PRELOAD(ptr) \
    		do { /* preload a cache line (Xscale Processor) */ \
    			_mbufPtrPrefetch(ptr); \
    		} \
    		while(0)
        #else /* _DIAB_TOOL is not defined */
            #define IX_ACC_DATA_CACHE_PRELOAD(ptr) \
    		do { /* preload a cache line (Xscale Processor) */ \
    			__asm__ (" pld [%0]\n": : "r" (ptr)); \
    		} \
    		while(0)
        #endif /* #ifdef _DIAB_TOOL */
	#else
		/* preload not implemented on different processor */
		#define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \
		do { /* nothing */ } while (0)
	#endif
#else
	/* preload not needed if cache is not enabled */
	#define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \
	do { /* nothing */ } while (0)
#endif

/**
 *
 * @brief function to retrieve the correct pointer from
 * a queue entry posted by the NPE
 *
 * @param qEntry : entry from qmgr queue 
 *        mask : applicable mask for this queue
 *        (4 most significant bits are used for additional informations)
 *
 * @return IX_OSAL_MBUF * pointer to mbuf header
 *
 * @internal
 */
PRIVATE IX_OSAL_MBUF * 
ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask)
{
    IX_OSAL_MBUF *mbufPtr;

    if (qEntry != 0)
    {
        /* mask NPE bits (e.g. priority, port ...) */
        qEntry &= mask;
        
#if defined(IX_ACC_DRAM_PHYS_OFFSET) && IX_ACC_DRAM_PHYS_OFFSET != 0
        /* restore the original address pointer (if PHYS_OFFSET is not 0) */
        qEntry |= (IX_ACC_DRAM_PHYS_OFFSET & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);
#endif
        /* get the mbuf pointer address from the npe-shared address */
        qEntry -= offsetof(IX_OSAL_MBUF,ix_ne);

        /* phys2virt mbuf */
        mbufPtr = (IX_OSAL_MBUF *)IX_OSAL_MMU_PHYS_TO_VIRT(qEntry);

        /* preload the cacheline shared with NPE */
        IX_ACC_DATA_CACHE_PRELOAD(IX_ETHACC_NE_SHARED(mbufPtr));

        /* preload the cacheline used by xscale */
        IX_ACC_DATA_CACHE_PRELOAD(mbufPtr); 
    }
    else
    {
	mbufPtr = NULL;
    }

    return mbufPtr;
}

/* Convert the mbuf header for NPE transmission */
PRIVATE UINT32 
ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf)
{
    UINT32 qbuf;
    UINT32 len;

    /* endianess swap for tci and flags
       note: this is done only once, even for chained buffers */
    IX_ETHACC_NE_FLAGS(mbuf)   = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf));
    IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf));

    /* test for unchained mbufs */
    if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL)
    {
	/* "best case" scenario : unchained mbufs */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxMBufs);

	/* payload pointer conversion */
	DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf));

	/* unchained mbufs : the frame length is the mbuf length 
	 * and the 2 identical lengths are stored in the same
	 * word.
	 */
	len = IX_OSAL_MBUF_MLEN(mbuf);

	/* set the length in both length and pktLen 16-bits fields */
	len |= (len << IX_ETHNPE_ACC_LENGTH_OFFSET);
	IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

	/* unchained mbufs : next contains 0 */
	IX_ETHACC_NE_NEXT(mbuf) = 0;

	/* flush shared header after all address conversions */
	IX_ETHACC_NE_CACHE_FLUSH(mbuf);
    }
    else
    {
	/* chained mbufs */
	IX_OSAL_MBUF *ptr = mbuf;
	IX_OSAL_MBUF *nextPtr;
	UINT32 frmLen;

	/* get the frame length from the header of the first buffer */
	frmLen = IX_OSAL_MBUF_PKT_LEN(mbuf);

	do
	{
	    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxMBufs);

	    /* payload pointer */
	    DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr));
	    /* Buffer length and frame length are stored in the same word */
	    len = IX_OSAL_MBUF_MLEN(ptr);
	    len = frmLen | (len << IX_ETHNPE_ACC_LENGTH_OFFSET);
	    IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

	    /* get the virtual next chain pointer */
	    nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);
	    if (nextPtr != NULL)
	    {
		/* shared pointer of the next buffer is chained */
		PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr),
			     IX_ETHACC_NE_NEXT(ptr));
	    }
	    else
	    {
		IX_ETHACC_NE_NEXT(ptr) = 0;
	    }

	    /* flush shared header after all address conversions */
	    IX_ETHACC_NE_CACHE_FLUSH(ptr);

	    /* move to next buffer */
	    ptr = nextPtr;

	    /* the frame length field is set only in the first buffer
	     * and is zeroed in the next buffers 
	     */
	    frmLen = 0;
	}
	while(ptr != NULL);

    }
    
    /* virt2phys mbuf itself */
    qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(
		  IX_ETHACC_NE_SHARED(mbuf));

    /* Ensure the bits which are reserved to exchange information with
     * the NPE are cleared 
     *
     * If the mbuf address is not correctly aligned, or from an
     * incompatible memory range, there is no point to continue
     */
    IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_TXENET_ADDR_MASK) == 0), 
	      "Invalid address range");

    return qbuf;
}

/* Convert the mbuf header for NPE reception */
PRIVATE UINT32 
ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf)
{
    UINT32 len;
    UINT32 qbuf;

    if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL)
    {
	/* "best case" scenario : unchained mbufs */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxFreeMBufs);

	/* unchained mbufs : payload pointer */
	DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf));

	/* unchained mbufs : set the buffer length 
	* and the frame length field is zeroed
	*/
	len = (IX_OSAL_MBUF_MLEN(mbuf) << IX_ETHNPE_ACC_LENGTH_OFFSET);
	IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

	/* unchained mbufs : next pointer is null */
	IX_ETHACC_NE_NEXT(mbuf) = 0;

	/* flush shared header after all address conversions */
	IX_ETHACC_NE_CACHE_FLUSH(mbuf);
	
	/* remove shared header cache line */
	IX_ETHACC_NE_CACHE_INVALIDATE(mbuf);
    }
    else
    {
	/* chained mbufs */
	IX_OSAL_MBUF *ptr = mbuf;
	IX_OSAL_MBUF *nextPtr;

	do
	{
	    /* chained mbufs */
	    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxFreeMBufs);

	    /* we must save virtual next chain pointer */
	    nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);

	    if (nextPtr != NULL)
	    {
		/* chaining pointer for NPE */
		PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr),
			     IX_ETHACC_NE_NEXT(ptr));
	    }
	    else
	    {
		IX_ETHACC_NE_NEXT(ptr) = 0;
	    }

	    /* payload pointer */
	    DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr));

	    /* buffer length */
	    len = (IX_OSAL_MBUF_MLEN(ptr) << IX_ETHNPE_ACC_LENGTH_OFFSET);
	    IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

	    /* flush shared header after all address conversions */
	    IX_ETHACC_NE_CACHE_FLUSH(ptr);

	    /* remove shared header cache line */
	    IX_ETHACC_NE_CACHE_INVALIDATE(ptr);

	    /* next mbuf in the chain */
	    ptr = nextPtr;
	}
	while(ptr != NULL);
    }

    /* virt2phys mbuf itself */
    qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(
		  IX_ETHACC_NE_SHARED(mbuf));

    /* Ensure the bits which are reserved to exchange information with
     * the NPE are cleared 
     *
     * If the mbuf address is not correctly aligned, or from an
     * incompatible memory range, there is no point to continue
     */
    IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK) == 0), 
	      "Invalid address range");

    return qbuf;
}

/* Convert the mbuf header after NPE transmission 
 * Since there is nothing changed by the NPE, there is no need 
 * to process anything but the update of internal stats
 * when they are enabled
*/
PRIVATE void 
ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf)
{
#ifndef NDEBUG
    /* test for unchained mbufs */
    if (IX_ETHACC_NE_NEXT(mbuf) == 0)
    {
	/* unchained mbufs : update the stats */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxDoneMBufs);
    }
    else
    {
	/* chained mbufs : walk the chain and update the stats */
	IX_OSAL_MBUF *ptr = mbuf;

	do
	{
	    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxDoneMBufs);
	    ptr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);
	}
	while (ptr != NULL);
    }
#endif
}

/* Convert the mbuf header after NPE reception */
PRIVATE void 
ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf)
{
    UINT32 len;

    /* endianess swap for tci and flags
       note: this is done only once, even for chained buffers */
    IX_ETHACC_NE_FLAGS(mbuf)   = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf));
    IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf));

    /* test for unchained mbufs */
    if (IX_ETHACC_NE_NEXT(mbuf) == 0)
    {
	/* unchained mbufs */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxMBufs);
    
	/* get the frame length. it is the same than the buffer length */
	len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf));
	len &= IX_ETHNPE_ACC_PKTLENGTH_MASK;
	IX_OSAL_MBUF_PKT_LEN(mbuf) = IX_OSAL_MBUF_MLEN(mbuf) = len;
	
        /* clears the next packet field */
	IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) = NULL;
    }
    else
    {
	IX_OSAL_MBUF *ptr = mbuf;
	IX_OSAL_MBUF *nextPtr = NULL;
	UINT32 frmLen;

	/* convert the frame length */
	frmLen = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf));
	IX_OSAL_MBUF_PKT_LEN(mbuf) = (frmLen & IX_ETHNPE_ACC_PKTLENGTH_MASK);
	
        /* chained mbufs */
	do
	{
	    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxMBufs);
    
	    /* convert the length */
	    len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(ptr));
	    IX_OSAL_MBUF_MLEN(ptr) = (len >> IX_ETHNPE_ACC_LENGTH_OFFSET);
	    
            /* get the next pointer */
 	    PTR_NPE2VIRT(IX_OSAL_MBUF *,IX_ETHACC_NE_NEXT(ptr), nextPtr);
	    if (nextPtr != NULL)
	    {
		nextPtr = (IX_OSAL_MBUF *)((UINT8 *)nextPtr - offsetof(IX_OSAL_MBUF,ix_ne));
	    }
	    /* set the next pointer */
	    IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr) = nextPtr;
	    
	    /* move to the next buffer */
	    ptr = nextPtr;
	}
	while (ptr != NULL);
    }
}

/* write to qmgr if possible and report an overflow if not possible 
 * Use a fast lock to protect the queue write.
 * This way, the tx feature is reentrant.
 */
PRIVATE IX_STATUS
ixEthAccQmgrLockTxWrite(IxEthAccPortId portId, UINT32 qBuffer)
{
    IX_STATUS qStatus;

    /* Try a lock on this port's TX queue 
     */
    if (ixOsalFastMutexTryLock(&txWriteMutex[portId]) \
         == IX_SUCCESS)
    {
	qStatus = ixQMgrQWrite(
	       IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
	       &qBuffer);
#ifndef NDEBUG  
	if (qStatus != IX_SUCCESS)
	{
	    TX_STATS_INC(portId, txOverflow);   
	}
#endif
	ixOsalFastMutexUnlock(&txWriteMutex[portId]);
    }
    else
    {
	TX_STATS_INC(portId, txLock);
	qStatus = IX_QMGR_Q_OVERFLOW;
    }
    return qStatus;
}

/* write to qmgr if possible and report an overflow if not possible 
 * Use a fast lock to protect the queue write.
 * This way, the Rx feature is reentrant.
 */
PRIVATE IX_STATUS
ixEthAccQmgrLockRxWrite(IxEthAccPortId portId, UINT32 qBuffer)
{
    IX_STATUS qStatus;
    if (ixOsalFastMutexTryLock(&rxWriteMutex[portId]) == IX_SUCCESS)
    {
	qStatus = ixQMgrQWrite(
	       IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId),
	       &qBuffer);	    
#ifndef NDEBUG  
	if (qStatus != IX_SUCCESS)
	{
	    RX_STATS_INC(portId, rxFreeOverflow);	    
	}
#endif
	ixOsalFastMutexUnlock(&rxWriteMutex[portId]);
    }
    else
    {
	RX_STATS_INC(portId, rxFreeLock);
	qStatus = IX_QMGR_Q_OVERFLOW;
    }
    return qStatus;
}

/* 
 * Set the priority and write to a qmgr queue.
 */
PRIVATE IX_STATUS
ixEthAccQmgrTxWrite(IxEthAccPortId portId, UINT32 qBuffer, UINT32 priority)
{
    /* fill the priority field */
    qBuffer |= (priority << IX_ETHNPE_QM_Q_FIELD_PRIOR_R);

    return ixEthAccQmgrLockTxWrite(portId, qBuffer);	    
}

/**
 *
 * @brief This function will discover the highest priority S/W Tx Q that
 *        has entries in it
 *
 * @param portId - (in) the id of the port whose S/W Tx queues are to be searched
 *        priorityPtr - (out) the priority of the highest priority occupied q will be written
 *                      here
 *
 * @return IX_ETH_ACC_SUCCESS if an occupied Q is found
 *         IX_ETH_ACC_FAIL if no Q has entries
 *
 * @internal
 */
PRIVATE IxEthAccStatus
ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId,
				IxEthAccTxPriority *priorityPtr)
{
    if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline 
	== FIFO_NO_PRIORITY)
    {
	if(IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
	       ixEthAccTxData.txQ[IX_ETH_ACC_TX_DEFAULT_PRIORITY]))
	{
	    return IX_ETH_ACC_FAIL;
	}
	else
	{
	    *priorityPtr = IX_ETH_ACC_TX_DEFAULT_PRIORITY;
	    TX_STATS_INC(portId,txPriority[*priorityPtr]);
	    return IX_ETH_ACC_SUCCESS;
	}
    }
    else
    {
	IxEthAccTxPriority highestPriority = IX_ETH_ACC_TX_PRIORITY_7;       
	while(1)
	{
	    if(!IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
	       ixEthAccTxData.txQ[highestPriority]))
	    {

		*priorityPtr = highestPriority;
		TX_STATS_INC(portId,txPriority[highestPriority]);
		return IX_ETH_ACC_SUCCESS;

	    }
	    if (highestPriority == IX_ETH_ACC_TX_PRIORITY_0)
	    {
		return IX_ETH_ACC_FAIL;
	    }
	    highestPriority--;
	}
    }
}

/**
 *
 * @brief This function will take a buffer from a TX S/W Q and attempt
 *        to add it to the relevant TX H/W Q
 *
 * @param portId - the port whose TX queue is to be written to
 *        priority - identifies the queue from which the entry is to be read
 *
 * @internal
 */
PRIVATE IxEthAccStatus
ixEthAccTxFromSwQ(IxEthAccPortId portId,
		  IxEthAccTxPriority priority)
{
    IX_OSAL_MBUF        *mbuf;
    IX_STATUS	   qStatus;

    IX_OSAL_ENSURE((UINT32)priority <= (UINT32)7, "Invalid priority");

    IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD(
	ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], 
	mbuf);
    
    if (mbuf != NULL)
    {
	/*
	 * Add the Tx buffer to the H/W Tx Q  
	 * We do not need to flush here as it is already done
	 * in TxFrameSubmit().
	 */
	qStatus = ixEthAccQmgrTxWrite(
	      portId, 
	      (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS((UINT32)IX_ETHACC_NE_SHARED(mbuf)),
	      priority);
	
	if (qStatus == IX_SUCCESS)
	{
	    TX_STATS_INC(portId,txFromSwQOK);
	    return IX_SUCCESS;
	}
	else if (qStatus == IX_QMGR_Q_OVERFLOW)
	{
	    /*
	     * H/W Q overflow, need to save the buffer 
	     * back on the s/w Q.
	     * we must put it back on the head of the q to avoid 
	     * reordering packet tx
	     */
	    TX_STATS_INC(portId,txFromSwQDelayed);
	    IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
		ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], 
		mbuf);

	    /*enable Q notification*/
	    qStatus = ixQMgrNotificationEnable(
		IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
		IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId));
        
            if (qStatus != IX_SUCCESS && qStatus != IX_QMGR_WARNING)
            {
		TX_INC(portId,txUnexpectedError);
		IX_ETH_ACC_FATAL_LOG(
	            "ixEthAccTxFromSwQ:Unexpected Error: %u\n", 
	            qStatus, 0, 0, 0, 0, 0);
            }
	}
	else 
	{
	    TX_INC(portId,txUnexpectedError);

	    /* recovery attempt */
	    IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
		ixEthAccPortData[portId].ixEthAccTxData.txQ[priority], 
		mbuf);
	    
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthAccTxFromSwQ:Error: unexpected QM status 0x%08X\n", 
		qStatus, 0, 0, 0, 0, 0);	
	}
    }
    else
    {
	/* sw queue is empty */
    }
    return IX_ETH_ACC_FAIL;
}

/**
 *
 * @brief This function will take a buffer from a RXfree S/W Q and attempt
 *        to add it to the relevant RxFree H/W Q
 *
 * @param portId - the port whose RXFree queue is to be written to
 *
 * @internal
 */
PRIVATE IxEthAccStatus
ixEthAccRxFreeFromSwQ(IxEthAccPortId portId)
{
    IX_OSAL_MBUF        *mbuf;
    IX_STATUS	   qStatus = IX_SUCCESS;

    IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD(
	  ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, 
	  mbuf);
    if (mbuf != NULL)
    {
	/*
	 * Add The Rx Buffer to the H/W Free buffer Q if possible  
	 */
	qStatus = ixEthAccQmgrLockRxWrite(portId, 
		  (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(
			 (UINT32)IX_ETHACC_NE_SHARED(mbuf)));

	if (qStatus == IX_SUCCESS)
	{
	    RX_STATS_INC(portId,rxFreeRepFromSwQOK);
	    /*
	     * Buffer added to h/w Q.
	     */
	    return IX_SUCCESS;
	}
	else if (qStatus == IX_QMGR_Q_OVERFLOW)
	{
	    /*
	     * H/W Q overflow, need to save the buffer back on the s/w Q.
	     */
	    RX_STATS_INC(portId,rxFreeRepFromSwQDelayed);

	    IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
		   ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, 
		   mbuf);
	}
	else 
	{
	    /* unexpected qmgr error */
	    RX_INC(portId,rxUnexpectedError);

	    IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
		    ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, 
		    mbuf);

	    IX_ETH_ACC_FATAL_LOG("IxEthAccRxFreeFromSwQ:Error: unexpected QM status 0x%08X\n", 
				 qStatus, 0, 0, 0, 0, 0);
	}
    }
    else
    {
	/* sw queue is empty */
    }
    return IX_ETH_ACC_FAIL;
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccInitDataPlane()
{	
    IxEthNpePhysicalId physicalId;
    
    /* 
     * Initialize the service and register callback to other services.
     */

    IX_ETH_ACC_MEMSET(&ixEthAccDataStats, 
		      0, 
		      sizeof(ixEthAccDataStats));

    /* init port data */
    for(physicalId=0; physicalId < IX_ETH_ACC_NUMBER_OF_PORTS; physicalId++)
    {
	ixOsalFastMutexInit(&txWriteMutex[physicalId]);
	ixOsalFastMutexInit(&rxWriteMutex[physicalId]);

	IX_ETH_ACC_MEMSET(&ixEthAccPortData[physicalId], 
			  0, 
			  sizeof(ixEthAccPortData[physicalId]));
	ixEthAccPortData[physicalId].ixEthAccTxData.schDiscipline = FIFO_NO_PRIORITY;
    }
    ixEthAccDataInfo.schDiscipline = FIFO_NO_PRIORITY;
    ixEthAccDataInfo.rxQMCbTypeSet = UNREGISTERED;
    ixEthAccDataInfo.npeCount = 0;

    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccPortTxDoneCallbackRegister(IxEthAccPortId portId, 
						  IxEthAccPortTxDoneCallback
						  txCallbackFn, 
						  UINT32 callbackTag)
{	
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    if (txCallbackFn == 0)
	/* Check for null function pointer here. */
    {
	return (IX_ETH_ACC_INVALID_ARG);
    }
    ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn = txCallbackFn;
    ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag = callbackTag;
    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccPortRxCallbackRegister(IxEthAccPortId portId, 
					      IxEthAccPortRxCallback
					      rxCallbackFn, 
					      UINT32 callbackTag)
{	
    IxEthAccPortId port;
    IxEthAccStatus status;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    
    /* Check for null function pointer here. */
    if (rxCallbackFn == NULL)
    {
	return (IX_ETH_ACC_INVALID_ARG);
    }

    /* Check the user is not changing the callback type
     * when the port is enabled. 
    */
    if (ixEthAccMacState[portId].portDisableState == ACTIVE)
    {
	for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++)
	{
	    if ((ixEthAccMacState[port].portDisableState == ACTIVE)
		&& (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == TRUE))
	    {
		/* one of the active ports has a different rx callback type.
		 * Changing the callback type when the port is enabled
		 * is not safe 
		 */
		return (IX_ETH_ACC_INVALID_ARG);
	    }
	}
    }

    /* update the callback pointer : this is done before 
     * registering the new qmgr callback
     */
    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn = rxCallbackFn;
    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag = callbackTag;
    
    if(ixEthAccDataInfo.rxQMCbTypeSet != NORMAL)
    {
        /* update the qmgr callback for rx queues 
           if priority scheduling is enabled for Rx, use the priority callback
         */
        if(ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY)
        {
            status = ixEthAccQMgrRxCallbacksRegister(
                         (IxQMgrCallback) ixEthRxPriorityPoll, 
                         IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK); 
        } else {
            status = ixEthAccQMgrRxCallbacksRegister(ixEthRxFrameQMCallback, 0); 
        }
        if(status != IX_ETH_ACC_SUCCESS)
        {
	    /* unexpected qmgr error */
            IX_ETH_ACC_FATAL_LOG("ixEthAccPortRxCallbackRegister: unexpected QMgr error, " \
                "could not register Rx single-buffer callback\n", 0, 0, 0, 0, 0, 0);
 
            RX_INC(portId,rxUnexpectedError);
            return (status);
        }
        ixEthAccDataInfo.rxQMCbTypeSet = NORMAL;
    }
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = FALSE;

    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccPortMultiBufferRxCallbackRegister(
			 IxEthAccPortId portId, 
			 IxEthAccPortMultiBufferRxCallback
			 rxCallbackFn, 
			 UINT32 callbackTag)
{
    IxEthAccPortId port;
    IxEthAccStatus status;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }
    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    
    /* Check for null function pointer here. */
    if (rxCallbackFn == NULL)
    {
	return (IX_ETH_ACC_INVALID_ARG);
    }

    /* Check the user is not changing the callback type
     * when the port is enabled. 
    */
    if (ixEthAccMacState[portId].portDisableState == ACTIVE)
    {
	for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++)
	{
	    if ((ixEthAccMacState[port].portDisableState == ACTIVE)
		&& (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == FALSE))
	    {
		/* one of the active ports has a different rx callback type.
		 * Changing the callback type when the port is enabled
		 * is not safe 
		 */
		return (IX_ETH_ACC_INVALID_ARG);
	    }
	}
    }

    /* update the callback pointer : this is done before 
     * registering the new qmgr callback
     */
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackFn = rxCallbackFn;
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackTag = callbackTag;

    if(ixEthAccDataInfo.rxQMCbTypeSet != MULTIBUFFER)
    {
        /* update the qmgr callback for rx queues 
           if priority scheduling is enabled for Rx, use the priority callback
         */
        if(ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY)
        {
            status = ixEthAccQMgrRxCallbacksRegister(
                         (IxQMgrCallback)ixEthRxMultiBufferPriorityPoll,
                         IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK);
        } else {
            status = ixEthAccQMgrRxCallbacksRegister(ixEthRxMultiBufferQMCallback, 0);
        }
        if(status != IX_ETH_ACC_SUCCESS)
        {
	    /* unexpected qmgr error */
	    RX_INC(portId,rxUnexpectedError);

            IX_ETH_ACC_FATAL_LOG("ixEthAccPortMultiBufferRxCallbackRegister: unexpected QMgr error, " \
                "could not register Rx multi-buffer callback\n", 0, 0, 0, 0, 0, 0);

	    return (IX_ETH_ACC_INVALID_ARG);
        }
        ixEthAccDataInfo.rxQMCbTypeSet = MULTIBUFFER;
    }
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = TRUE;

    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccPortTxFrameSubmit(IxEthAccPortId portId, 
					 IX_OSAL_MBUF *buffer, 
					 IxEthAccTxPriority priority)
{	
    IX_STATUS	qStatus = IX_SUCCESS;
    UINT32      qBuffer;
    IxEthAccTxPriority highestPriority;
    IxQMgrQStatus txQStatus;

#ifndef NDEBUG 
    if (buffer == NULL)
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    if ((UINT32)priority > (UINT32)IX_ETH_ACC_TX_PRIORITY_7)
    {
	return (IX_ETH_ACC_INVALID_ARG);
    }
#endif 

    /*
     * Need to Flush the MBUF and its contents (data) as it may be
     * read from the NPE. Convert virtual addresses to physical addresses also.
     */
    qBuffer = ixEthAccMbufTxQPrepare(buffer);

    /*
     * If no fifo priority set on Xscale ...
     */
    if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == 
	FIFO_NO_PRIORITY)
    {
	/*
	 * Add The Tx Buffer to the H/W Tx Q if possible  
	 * (the priority is passed to the NPE, because
	 * the NPE is able to reorder the frames 
	 * before transmission to the underlying hardware)
	 */
	qStatus = ixEthAccQmgrTxWrite(portId,
				      qBuffer,
				      IX_ETH_ACC_TX_DEFAULT_PRIORITY);

	if (qStatus == IX_SUCCESS)
	{
	    TX_STATS_INC(portId,txQOK);

	    /*  
	     * "best case" scenario : Buffer added to h/w Q.
	     */
	    return (IX_SUCCESS);
	}
	else if (qStatus == IX_QMGR_Q_OVERFLOW)
	{
	    /*
	     * We were unable to write the buffer to the 
	     * appropriate H/W Q,  Save it in the sw Q.
	     * (use the default priority queue regardless of 
	     * input parameter)
	     */
	    priority = IX_ETH_ACC_TX_DEFAULT_PRIORITY;
	}
	else 
	{
	    /* unexpected qmgr error */
	    TX_INC(portId,txUnexpectedError);
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthAccPortTxFrameSubmit:Error: qStatus = %u\n", 
		(UINT32)qStatus, 0, 0, 0, 0, 0);
	    return (IX_ETH_ACC_FAIL);
	}
    }
    else if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == 
	     FIFO_PRIORITY)
    {
	
	/*
	 * For priority transmission, put the frame directly on the H/W queue
	 * if the H/W queue is empty, otherwise, put it in a S/W Q
	 */
	ixQMgrQStatusGet(IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), &txQStatus);
	if((txQStatus & IX_QMGR_Q_STATUS_E_BIT_MASK) != 0)
	{
	    /*The tx queue is empty, check whether there are buffers on the s/w queues*/
	    if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
	       !=IX_ETH_ACC_FAIL)
	    {
		/*there are buffers on the s/w queues, submit them*/
		ixEthAccTxFromSwQ(portId, highestPriority);

		/* the queue was empty, 1 buffer is already supplied
		 * but is likely to be immediately transmitted and the 
		 * hw queue is likely to be empty again, so submit 
		 * more from the sw queues
		 */
		if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
		   !=IX_ETH_ACC_FAIL)
		{
		    ixEthAccTxFromSwQ(portId, highestPriority);
		    /*
		     * and force the buffer supplied to be placed 
		     * on a priority queue
		     */
		    qStatus = IX_QMGR_Q_OVERFLOW;
		}
		else
		{
		    /*there are no buffers in the s/w queues, submit directly*/
		    qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority);
		}
	    }
	    else
	    {
		/*there are no buffers in the s/w queues, submit directly*/
		qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority);
	    }
	}
	else
	{
	    qStatus = IX_QMGR_Q_OVERFLOW;
	}
    }
    else 
    {
	TX_INC(portId,txUnexpectedError);
	IX_ETH_ACC_FATAL_LOG(
	    "ixEthAccPortTxFrameSubmit:Error: wrong schedule discipline setup\n", 
	    0, 0, 0, 0, 0, 0);
	return (IX_ETH_ACC_FAIL);
    }
    
    if(qStatus == IX_SUCCESS ) 
    {
	TX_STATS_INC(portId,txQOK);
	return IX_ETH_ACC_SUCCESS;
    }
    else if(qStatus == IX_QMGR_Q_OVERFLOW)
    {
	TX_STATS_INC(portId,txQDelayed);
	/*
	 * We were unable to write the buffer to the 
	 * appropriate H/W Q,  Save it in a s/w Q.
	 */
	IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL(
		ixEthAccPortData[portId].
		ixEthAccTxData.txQ[priority], 
		buffer);
	    
	qStatus = ixQMgrNotificationEnable(
		IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
		IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId));

        if (qStatus != IX_SUCCESS)
	{
	    if (qStatus == IX_QMGR_WARNING)
	    {
		/* notification is enabled for a queue 
		 * which is already empty (the condition is already met)
		 * and there will be no more queue event to drain the sw queue
		 */
		TX_STATS_INC(portId,txLateNotificationEnabled);
		
		/* pull a buffer from the sw queue */
		if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
		   !=IX_ETH_ACC_FAIL)
		{
		    /*there are buffers on the s/w queues, submit from them*/
		    ixEthAccTxFromSwQ(portId, highestPriority);
		}
	    }
	    else
	    {
		TX_INC(portId,txUnexpectedError);
		IX_ETH_ACC_FATAL_LOG(
		     "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n", 
		     qStatus, 0, 0, 0, 0, 0);
	    }
        }
    }
    else
    {
	TX_INC(portId,txUnexpectedError);
	IX_ETH_ACC_FATAL_LOG(
	     "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n", 
	     qStatus, 0, 0, 0, 0, 0);
	return (IX_ETH_ACC_FAIL);
    }

    return (IX_ETH_ACC_SUCCESS);
}


/**
 *
 * @brief replenish: convert a chain of mbufs to the format 
 *        expected by the NPE
 *
  */

IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccPortRxFreeReplenish(IxEthAccPortId portId, 
					   IX_OSAL_MBUF *buffer)
{	
    IX_STATUS	qStatus = IX_SUCCESS;
    UINT32      qBuffer;
    
    /* 
     * Check buffer is valid.
     */
    
#ifndef NDEBUG 
    /* check parameter value */
    if (buffer == 0)
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }

    /* check initialisation is done */
    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    /* check boundaries and constraints */
    if (IX_OSAL_MBUF_MLEN(buffer) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN)
    {
	return (IX_ETH_ACC_FAIL);
    }
#endif 

    qBuffer = ixEthAccMbufRxQPrepare(buffer);

    /*
     * Add The Rx Buffer to the H/W Free buffer Q if possible  
     */
    qStatus = ixEthAccQmgrLockRxWrite(portId, qBuffer);

    if (qStatus == IX_SUCCESS)
    {
	RX_STATS_INC(portId,rxFreeRepOK);
	/* 
	 * Buffer added to h/w Q.
	 */
	return (IX_SUCCESS);
    }
    else if (qStatus == IX_QMGR_Q_OVERFLOW)
    {
	RX_STATS_INC(portId,rxFreeRepDelayed);
	/*
	 * We were unable to write the buffer to the approprate H/W Q, 
	 * Save it in a s/w Q.
	 */
	IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL(
	    ixEthAccPortData[portId].ixEthAccRxData.freeBufferList, 
	    buffer);

	qStatus = ixQMgrNotificationEnable(
	    IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId),
	    IX_ETH_ACC_PORT_TO_RX_FREE_Q_SOURCE(portId));
        
        if (qStatus != IX_SUCCESS)
	{
	    if (qStatus == IX_QMGR_WARNING)
	    {
		/* notification is enabled for a queue 
		 * which is already empty (the condition is already met)
		 * and there will be no more queue event to drain the sw queue
		 * move an entry from the sw queue to the hw queue */
		RX_STATS_INC(portId,rxFreeLateNotificationEnabled);
		ixEthAccRxFreeFromSwQ(portId);
	    }
	    else
	    {
		RX_INC(portId,rxUnexpectedError);
		IX_ETH_ACC_FATAL_LOG(
		     "ixEthAccRxPortFreeReplenish:Error: %u\n", 
		     qStatus, 0, 0, 0, 0, 0);
	    }
        }
    }
    else 
    {
	RX_INC(portId,rxUnexpectedError);
	IX_ETH_ACC_FATAL_LOG(
	    "ixEthAccRxPortFreeReplenish:Error: qStatus = %u\n", 
	    (UINT32)qStatus, 0, 0, 0, 0, 0);
        return(IX_ETH_ACC_FAIL);
    }
    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccTxSchedulingDisciplineSetPriv(IxEthAccPortId portId, 
						 IxEthAccSchedulerDiscipline
						 sched)
{	
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	return (IX_ETH_ACC_INVALID_PORT);
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
	return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }

    if (sched != FIFO_PRIORITY && sched != FIFO_NO_PRIORITY)
    {
	return (IX_ETH_ACC_INVALID_ARG);
    }

    ixEthAccPortData[portId].ixEthAccTxData.schDiscipline = sched;
    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccRxSchedulingDisciplineSetPriv(IxEthAccSchedulerDiscipline
						 sched)
{
    int port;

    /* cannot change the scheduling discipline while any port is active */
    for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++)
    {
        if (ixEthAccMacState[port].portDisableState == ACTIVE)
        {
            return IX_ETH_ACC_FAIL;
        }
    }

    switch(sched)
    {
        case  FIFO_PRIORITY:
            /* priority scheduling discipline not supported on IXP42X A0 HW */
            if( (IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X == ixFeatureCtrlDeviceRead ())
                && (IX_FEATURE_CTRL_SILICON_TYPE_A0 ==
               (ixFeatureCtrlProductIdRead() & IX_FEATURE_CTRL_SILICON_STEPPING_MASK)))
            {
	        return (IX_ETH_ACC_FAIL);
            } 
            if(ixEthAccDataInfo.rxQMCbTypeSet == NORMAL)
            {
                ixEthAccQMgrRxCallbacksRegister(
                             (IxQMgrCallback)ixEthRxPriorityPoll,
                             IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK);
            }
            else if(ixEthAccDataInfo.rxQMCbTypeSet == MULTIBUFFER)
            {
                ixEthAccQMgrRxCallbacksRegister(
                             (IxQMgrCallback)ixEthRxMultiBufferPriorityPoll,
                             IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK);
            }
            /* enable sticky interrupts */
            ixQMgrStickyInterruptRegEnable();
            break;
        case FIFO_NO_PRIORITY:
            if(ixEthAccDataInfo.rxQMCbTypeSet == NORMAL)
            {
                ixEthAccQMgrRxCallbacksRegister(ixEthRxFrameQMCallback,0);
                             
            }
            else if(ixEthAccDataInfo.rxQMCbTypeSet == MULTIBUFFER)
            {
                ixEthAccQMgrRxCallbacksRegister(ixEthRxMultiBufferQMCallback,0);
            }
            break;
        default:
	    return (IX_ETH_ACC_INVALID_ARG);
    }
    ixEthAccDataInfo.schDiscipline = sched;
    return (IX_ETH_ACC_SUCCESS);
}


/**
 * @fn ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr)
 *
 * @brief process incoming frame : 
 *
 * @param @ref IxQMgrCallback IxQMgrMultiBufferCallback
 *
 * @return none
 *
 * @internal
 *
 */
IX_ETH_ACC_PRIVATE BOOL 
ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr)
{	
    UINT32 flags;
    IxEthDBStatus result;

#ifndef NDEBUG
    /* Prudent to at least check the port is within range */
    if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS)
    {
	ixEthAccDataStats.unexpectedError++;
	IX_ETH_ACC_FATAL_LOG(
	     "ixEthRxFrameProcess: Illegal port: %u\n", 
	     (UINT32)portId, 0, 0, 0, 0, 0);
	return FALSE;
    }
#endif

    /* convert fields from mbuf header */
    ixEthAccMbufFromRxQ(mbufPtr);

    /* check about any special processing for this frame */
    flags = IX_ETHACC_NE_FLAGS(mbufPtr);
    if ((flags & (IX_ETHACC_NE_FILTERMASK | IX_ETHACC_NE_NEWSRCMASK)) == 0)
    {
	/* "best case" scenario : nothing special to do for this frame */
	return TRUE;
    }

    /* if a new source MAC address is detected by the NPE, 
     * update IxEthDB with the portId and the MAC address.
     */
    if ((flags & IX_ETHACC_NE_NEWSRCMASK & ixEthAccNewSrcMask) != 0)
    {
        result = ixEthDBFilteringDynamicEntryProvision(portId,
                          (IxEthDBMacAddr *) IX_ETHACC_NE_SOURCEMAC(mbufPtr));

	if (result != IX_ETH_DB_SUCCESS && result != IX_ETH_DB_FEATURE_UNAVAILABLE)
	{
            if ((ixEthAccMacState[portId].portDisableState == ACTIVE) && (result != IX_ETH_DB_BUSY))
            {
	        RX_STATS_INC(portId, rxUnexpectedError);
                IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to add source MAC to the Learning/Filtering database\n", 0, 0, 0, 0, 0, 0); 
            }
            else
            {
                /* we expect this to fail during PortDisable, as EthDB is disabled for
                 * that port and will refuse to learn new addresses 
		 */
            }
	}
	else
	{
	    RX_STATS_INC(portId, rxUnlearnedMacAddress);
	}
    }

    /* check if this frame should have been filtered
     * by the NPE and take the appropriate action 
     */
    if (((flags & IX_ETHACC_NE_FILTERMASK) != 0)
        && (ixEthAccMacState[portId].portDisableState == ACTIVE))
    {
        /* If the mbuf was allocated with a small data size, or the current data pointer is not
         * within the allocated data area, then the buffer is non-standard and has to be 
         * replenished with the minimum size only
         */
        if( (IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN)
           || ((UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) > IX_OSAL_MBUF_MDATA(mbufPtr))
           || ((UINT8 *)(IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) +
              IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr))
               < IX_OSAL_MBUF_MDATA(mbufPtr)) )
        {
            /* set to minimum length */
            IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) =
                IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN;
        }
        else
        {
            /* restore original length */
            IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) =
                ( IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) -
                 (IX_OSAL_MBUF_MDATA(mbufPtr) - (UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr)) );
        }

        /* replenish from here */
        if (ixEthAccPortRxFreeReplenish(portId, mbufPtr) != IX_ETH_ACC_SUCCESS)
        {
                IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to replenish with filtered frame\
                                      on port %d\n", portId, 0, 0, 0, 0, 0);
        }

        RX_STATS_INC(portId, rxFiltered);

        /* indicate that frame should not be subjected to further processing */
        return FALSE;
    }

    return TRUE;
}

/**
 * @fn ixEthRxPriorityPoll 
 *
 * @brief RX routine to get limited entries from priority queues
 *
 * Routine may be called by user, or used as RX QM callback.
 * Receive queues are processed in priority order.
 *
 * @param reserved : placeholder for QM callback usage, ignored
 *        maxQEntries : maximum number of q entries to process 
 *
 * @return : number of frames processed
 *
 * @internal
 *
 * Design notes : 
 * While processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
UINT32 ixEthRxPriorityPoll(UINT32 reserved, UINT32 maxQEntries)
{	
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    IxQMgrQId  qId;
    UINT32     qIndex;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     destPortId;
    UINT32     rxQReadStatus;
    UINT32     qEntriesProcessed = 0;

    /*
     * Design note : entries are read in a buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];

    
    /* 
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);
    for(qId = ixEthAccDataInfo.rxQueues[qIndex = 0]; 
        qId < IX_QMGR_MAX_NUM_QUEUES && qEntriesProcessed < maxQEntries; 
        qId = ixEthAccDataInfo.rxQueues[++qIndex]) 
    {
        UINT32 qReadCount = maxQEntries - qEntriesProcessed;
	/* 
	 * Indication of the number of times the queue is drained
	 */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

	/* ensure the last entry of the array contains a zeroed value */
	qEntryPtr = rxQEntry;
	qEntryPtr[qReadCount] = 0;
	
	rxQReadStatus = ixQMgrQBurstRead(qId, 
		 qReadCount, 
		 qEntryPtr);

#ifndef NDEBUG
	if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
	    && (rxQReadStatus != IX_SUCCESS))
	{
	    ixEthAccDataStats.unexpectedError++;
	    /*major error*/
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthRxPriorityPoll:Error: %u\n", 
		(UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
	    return qEntriesProcessed;
	}
#endif

	/* convert and preload the next entry
	 * (the conversion function takes care about null pointers which
	 * are used to mark the end of the loop)
	 */
	nextQEntry = *qEntryPtr;
	nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			  IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	while(nextQEntry != 0)
	{
	    /* get the next entry */
	    qEntry = nextQEntry;
	    mbufPtr = nextMbufPtr;

#ifndef NDEBUG
	    if (mbufPtr == NULL)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthRxPriorityPoll: Null Mbuf Ptr\n",
		    0, 0, 0, 0, 0, 0);
		return qEntriesProcessed;
	    }
#endif
            
	    /* convert the next entry
	     * (the conversion function takes care about null pointers which
	     * are used to mark the end of the loop)
	     */
	    nextQEntry = *(++qEntryPtr);
	    nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			      IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	    /*
	     * Get Port ID from message.
	     */	    
	    portId = IX_ETHNPE_NODE_AND_PORT_TO_PHYSICAL_ID(IX_ETHNPE_QM_Q_RXENET_NPEID_VAL(qEntry),IX_ETHNPE_QM_Q_PID_VAL(qEntry));

	    /* process frame, check the return code and skip the remaining of
	     * the loop if the frame is to be filtered out 
	     */
            if (ixEthRxFrameProcess(portId, mbufPtr))
            {
	        /* destination portId for this packet */
	        destPortId = IX_ETHACC_NE_DESTPORTID(mbufPtr);

                if (destPortId != IX_ETH_DB_UNKNOWN_PORT)
                {
                    destPortId = IX_ETHNPE_LOGICAL_ID_TO_PHYSICAL_ID(destPortId);
                }

	        /* 
	        * increment priority stats 
	        */
	        RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]);

	        /*
	        * increment callback count stats
	        */
	        RX_STATS_INC(portId,rxFrameClientCallback);

	        /* 
	        * Call user level callback.
	        */
	        ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn(
		    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag, 
		    mbufPtr,
		    destPortId);
                /*increment total number of entries processed*/
                qEntriesProcessed++;
            }
	}
    }
    return qEntriesProcessed;
}

/**
 * @fn ixEthRxMultiBufferPriorityPoll 
 *
 * @brief RX routine to get limited entries from priority queues
 *
 * Routine may be called by user, or used as RX QM callback.
 * Receive queues are processed in priority order.
 * Buffers are passed as an array to the user callback
 *
 * @param reserved : placeholder for QM callback usage, ignored
 *        maxQEntries : maximum number of q entries to process 
 *
 * @return : number of frames processed
 *
 * @internal
 *
 * Design notes : 
 * While processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
UINT32 ixEthRxMultiBufferPriorityPoll(UINT32 reserved, UINT32 maxQEntries)
{	
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    IxQMgrQId  qId;
    UINT32     qIndex;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     rxQReadStatus;
    UINT32     qEntriesProcessed = 0;

    /*
     * Design note : entries are read in a buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];
    static IX_OSAL_MBUF *rxMbufPortArray[IX_ETH_ACC_NUMBER_OF_PORTS][IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];
    IX_OSAL_MBUF **rxMbufPtr[IX_ETH_ACC_NUMBER_OF_PORTS];

    for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
        rxMbufPtr[portId] = rxMbufPortArray[portId];
    }

    
    /* 
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);

    for(qId = ixEthAccDataInfo.rxQueues[qIndex = 0]; 
        qId < IX_QMGR_MAX_NUM_QUEUES && qEntriesProcessed < maxQEntries; 
        qId = ixEthAccDataInfo.rxQueues[++qIndex]) 
    {
        UINT32 qReadCount = maxQEntries - qEntriesProcessed;
	/* 
	 * Indication of the number of times the queue is drained
	 */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

	/* ensure the last entry of the array contains a zeroed value */
	qEntryPtr = rxQEntry;
	qEntryPtr[qReadCount] = 0;
	
	rxQReadStatus = ixQMgrQBurstRead(qId, 
		 qReadCount, 
		 qEntryPtr);

#ifndef NDEBUG
	if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
	    && (rxQReadStatus != IX_SUCCESS))
	{
	    ixEthAccDataStats.unexpectedError++;
	    /*major error*/
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthRxMultiBufferPriorityPoll:Error: %u\n", 
		(UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
	    return qEntriesProcessed;
	}
#endif

	/* convert and preload the next entry
	 * (the conversion function takes care about null pointers which
	 * are used to mark the end of the loop)
	 */
	nextQEntry = *qEntryPtr;
	nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			  IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	while(nextQEntry != 0)
	{
	    /* get the next entry */
	    qEntry = nextQEntry;
	    mbufPtr = nextMbufPtr;

#ifndef NDEBUG
	    if (mbufPtr == NULL)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthRxMultiBufferPriorityPoll: Null Mbuf Ptr\n",
		    0, 0, 0, 0, 0, 0);
		return qEntriesProcessed;
	    }
#endif
            
	    /* convert the next entry
	     * (the conversion function takes care about null pointers which
	     * are used to mark the end of the loop)
	     */
	    nextQEntry = *(++qEntryPtr);
	    nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			      IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	    /*
	     * Get Port ID from message.
	     */	    
	    portId = IX_ETHNPE_NODE_AND_PORT_TO_PHYSICAL_ID(IX_ETHNPE_QM_Q_RXENET_NPEID_VAL(qEntry),IX_ETHNPE_QM_Q_PID_VAL(qEntry));

	    /* process frame, check the return code and skip the remaining of
	     * the loop if the frame is to be filtered out 
	     */
            if (ixEthRxFrameProcess(portId, mbufPtr))
            {
                /* store a mbuf pointer in an array */
                *rxMbufPtr[portId]++ = mbufPtr;

                /* 
                 * increment priority stats 
                 */
                RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]);

                qEntriesProcessed++;
            }
	}
    }
    /* check if any of the the arrays contains any entry */
    for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
        if (rxMbufPtr[portId] != rxMbufPortArray[portId])
        {
            /* add a last NULL pointer at the end of the 
             * array of mbuf pointers 
             */
            *rxMbufPtr[portId] = NULL;

            /*
             * increment callback count stats
             */
            RX_STATS_INC(portId,rxFrameClientCallback);

            /* 
             * Call user level callback with an array of
             * buffers (NULL terminated)
             */
            ixEthAccPortData[portId].ixEthAccRxData.
                rxMultiBufferCallbackFn(
                        ixEthAccPortData[portId].ixEthAccRxData.
                               rxMultiBufferCallbackTag,
                        rxMbufPortArray[portId]);

            /* reset the buffer pointer to the beginning of
             * the array
             */
            rxMbufPtr[portId] = rxMbufPortArray[portId];
        }
    }
    return qEntriesProcessed;
}

/**
 * @fn ixEthRxFrameQMCallback 
 *
 * @brief receive callback for Frame receive Q from NPE
 *
 * Frames are passed one-at-a-time to the user
 *
 * @param @ref IxQMgrCallback
 *
 * @return none
 *
 * @internal
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
void ixEthRxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{	
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     destPortId;
    UINT32     rxQReadStatus;


    /*
     * Design note : entries are read in a buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];

    /* 
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);
    do
    {
	/* 
	 * Indication of the number of times the queue is drained
	 */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

	/* ensure the last entry of the array contains a zeroed value */
	qEntryPtr = rxQEntry;
	qEntryPtr[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK] = 0;
	
	rxQReadStatus = ixQMgrQBurstRead(qId, 
		 IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK, 
		 qEntryPtr);

#ifndef NDEBUG
	if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
	    && (rxQReadStatus != IX_SUCCESS))
	{
	    ixEthAccDataStats.unexpectedError++;
	    /*major error*/
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthRxFrameQMCallback:Error: %u\n", 
		(UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
	    return;
	}
#endif

	/* convert and preload the next entry
	 * (the conversion function takes care about null pointers which
	 * are used to mark the end of the loop)
	 */
	nextQEntry = *qEntryPtr;
	nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			  IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	while(nextQEntry != 0)
	{
	    /* get the next entry */
	    qEntry = nextQEntry;
	    mbufPtr = nextMbufPtr;

#ifndef NDEBUG
	    if (mbufPtr == NULL)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthRxFrameQMCallback: Null Mbuf Ptr\n",
		    0, 0, 0, 0, 0, 0);
		return;
	    }
#endif
            
	    /* convert the next entry
	     * (the conversion function takes care about null pointers which
	     * are used to mark the end of the loop)
	     */
	    nextQEntry = *(++qEntryPtr);
	    nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			      IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	    /*
	     * Get Port ID from message.
	     */	    
	    portId = IX_ETHNPE_NODE_AND_PORT_TO_PHYSICAL_ID(IX_ETHNPE_QM_Q_RXENET_NPEID_VAL(qEntry),IX_ETHNPE_QM_Q_PID_VAL(qEntry));
	    /* process frame, check the return code and skip the remaining of
	     * the loop if the frame is to be filtered out 
	     */
            if (ixEthRxFrameProcess(portId, mbufPtr))
            {
	        /* destination portId for this packet */
	        destPortId = IX_ETHACC_NE_DESTPORTID(mbufPtr);

                if (destPortId != IX_ETH_DB_UNKNOWN_PORT)
                {
                    destPortId = IX_ETHNPE_LOGICAL_ID_TO_PHYSICAL_ID(destPortId);
                }

	        /*
	        * increment callback count stats
	        */
	        RX_STATS_INC(portId,rxFrameClientCallback);

	        /* 
	        * Call user level callback.
	        */
	        ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn(
		    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag, 
		    mbufPtr,
		    destPortId);
            }
	}
    } while (rxQReadStatus == IX_SUCCESS);
}

/**
 * @fn ixEthRxMultiBufferQMCallback 
 *
 * @brief receive callback for Frame receive Q from NPE
 *
 * Frames are passed as an array to the user
 *
 * @param @ref IxQMgrCallback
 *
 * @return none
 *
 * @internal
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
void ixEthRxMultiBufferQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{	
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     rxQReadStatus;
    /*
     * Design note : entries are read in a static buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    static UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];
    static IX_OSAL_MBUF *rxMbufPortArray[IX_ETH_ACC_NUMBER_OF_PORTS][IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK + 1];
    IX_OSAL_MBUF **rxMbufPtr[IX_ETH_ACC_NUMBER_OF_PORTS];
    
    for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	rxMbufPtr[portId] = rxMbufPortArray[portId];
    }

    /* 
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);

    do
    {
	/* 
	 * Indication of the number of times the queue is drained
	 */
	IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

	/* ensure the last entry of the array contains a zeroed value */
	qEntryPtr = rxQEntry;
	qEntryPtr[IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK] = 0;

	rxQReadStatus = ixQMgrQBurstRead(qId, 
		 IX_ETH_ACC_MAX_RX_MBUF_CONSUME_PER_CALLBACK, 
		 qEntryPtr);

#ifndef NDEBUG
	if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
	    && (rxQReadStatus != IX_SUCCESS))
	{
	    ixEthAccDataStats.unexpectedError++;
	    /*major error*/
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthRxFrameMultiBufferQMCallback:Error: %u\n", 
		(UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
	    return;
	}
#endif

	/* convert and preload the next entry
	 * (the conversion function takes care about null pointers which
	 * are used to mark the end of the loop)
	 */
	nextQEntry = *qEntryPtr;
	nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			  IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	while(nextQEntry != 0)
	{
	    /* get the next entry */
	    qEntry = nextQEntry;
	    mbufPtr = nextMbufPtr;

#ifndef NDEBUG
	    if (mbufPtr == NULL)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthRxFrameMultiBufferQMCallback:Error: Null Mbuf Ptr\n", 
		    0, 0, 0, 0, 0, 0);
		return;
	    }
#endif

	    /* convert the next entry
	     * (the conversion function takes care about null pointers which
	     * are used to mark the end of the loop)
	     */
	    nextQEntry = *(++qEntryPtr);
	    nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry, 
			      IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

	    /*
	     * Get Port ID from message.
	     */	     
	    portId = IX_ETHNPE_NODE_AND_PORT_TO_PHYSICAL_ID(IX_ETHNPE_QM_Q_RXENET_NPEID_VAL(qEntry),IX_ETHNPE_QM_Q_PID_VAL(qEntry));

	    /* skip the remaining of the loop if the frame is 
	     * to be filtered out 
	     */
	    if (ixEthRxFrameProcess(portId, mbufPtr))
	    {
		/* store a mbuf pointer in an array */
		*rxMbufPtr[portId]++ = mbufPtr;
		
		/* 
		 * increment priority stats 
		 */
		RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]);
	    }
		
	}

	/* check if any of the the arrays contains any entry */
	for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
	{
	    if (rxMbufPtr[portId] != rxMbufPortArray[portId])
	    {
		/* add a last NULL pointer at the end of the 
		 * array of mbuf pointers 
		 */
		*rxMbufPtr[portId] = NULL;

		/*
		 * increment callback count stats
		 */
		RX_STATS_INC(portId,rxFrameClientCallback);

		/* 
		 * Call user level callback with an array of
		 * buffers (NULL terminated)
		 */
		ixEthAccPortData[portId].ixEthAccRxData.
		    rxMultiBufferCallbackFn(
			    ixEthAccPortData[portId].ixEthAccRxData.
			           rxMultiBufferCallbackTag, 
			    rxMbufPortArray[portId]);

		/* reset the buffer pointer to the beginning of
		 * the array
		 */
		rxMbufPtr[portId] = rxMbufPortArray[portId];
	    }
	}

    } while (rxQReadStatus == IX_SUCCESS);
}


/**
 * @brief  rxFree low event handler
 *
 */
void ixEthRxFreeQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{	
    IxEthAccPortId	portId = (IxEthAccPortId) callbackId;
    int		        lockVal;
    UINT32		maxQWritesToPerform = IX_ETH_ACC_MAX_RX_FREE_BUFFERS_LOAD;
    IX_STATUS	        qStatus = IX_SUCCESS;

    /*
     * We have reached a low threshold on one of the Rx Free Qs
     */
    
    /*note that due to the fact that we are working off an Empty threshold, this callback
      need only write a single entry to the Rx Free queue in order to re-arm the notification
    */
    
    RX_STATS_INC(portId,rxFreeLowCallback);
    
    /* 
     * Get buffers from approprite S/W Rx freeBufferList Q.
     */
    
#ifndef NDEBUG 
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	ixEthAccDataStats.unexpectedError++;
	IX_ETH_ACC_FATAL_LOG(
	    "ixEthRxFreeQMCallback:Error: Invalid Port 0x%08X\n", 
	    portId, 0, 0, 0, 0, 0);
	return;
    }
#endif
    IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);
    if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
					ixEthAccRxData.freeBufferList))
    {
	/*  
	 * Turn off Q callback notification for Q in Question.
	 */
	qStatus = ixQMgrNotificationDisable(
	    IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId));
	

	IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);

	if (qStatus != IX_SUCCESS)
	{
	    RX_INC(portId,rxUnexpectedError);
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthRxFreeQMCallback:Error: unexpected QM status 0x%08X\n", 
		qStatus, 0, 0, 0, 0, 0);
	    return;
	}
    }
    else 
    {
	IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
	/*
	 * Load the H/W Q with buffers from the s/w Q.
	 */
	
	do
	{
	    /* 
	     * Consume Q entries. - Note Q contains Physical addresss, 
	     * and have already been flushed to memory,
	     * And endianess converted if required.
	     */
	    if (ixEthAccRxFreeFromSwQ(portId) != IX_SUCCESS)
	    {
		/*
		 * No more entries in s/w Q.
		 * Turn off Q callback indication
		 */
		
		IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);
		if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
		    ixEthAccRxData.freeBufferList))
		{
		    qStatus = ixQMgrNotificationDisable(
			IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId));
		}
		IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
		break;
	    }
	}
	while (--maxQWritesToPerform);
    }
}
/**
 * @fn Tx queue low event handler
 *
 */
void 
ixEthTxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IxEthAccPortId portId = (IxEthAccPortId) callbackId;
    int		   lockVal;
    UINT32	   maxQWritesToPerform = IX_ETH_ACC_MAX_TX_FRAME_TX_CONSUME_PER_CALLBACK;
    IX_STATUS	   qStatus = IX_SUCCESS;
    IxEthAccTxPriority highestPriority;

    
    /*
     * We have reached a low threshold on the Tx Q, and are being asked to 
     * supply a buffer for transmission from our S/W TX queues
     */
    TX_STATS_INC(portId,txLowThreshCallback);

    /* 
     * Get buffers from approprite Q.
     */
    
#ifndef NDEBUG
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
	ixEthAccDataStats.unexpectedError++;
	IX_ETH_ACC_FATAL_LOG(
	    "ixEthTxFrameQMCallback:Error: Invalid Port 0x%08X\n", 
	    portId, 0, 0, 0, 0, 0);
	return;
    }
#endif

    do
    {
	/* 
	 * Consume Q entries. - Note Q contains Physical addresss, 
	 * and have already been flushed to memory,
	 * and endianess already sone if required.
	 */
	
	IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);
	
	if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) ==
	   IX_ETH_ACC_FAIL)
	{
	    /*
	     * No more entries in s/w Q.
	     * Turn off Q callback indication
	     */
	    qStatus = ixQMgrNotificationDisable(
		IX_ETH_ACC_PORT_TO_TX_Q_ID(portId));

	    IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);

	    if (qStatus != IX_SUCCESS)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthTxFrameQMCallback:Error: unexpected QM status 0x%08X\n", 
		    qStatus, 0, 0, 0, 0, 0);
	    }
	    
	    return;	    
	}
	else
	{
	    IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
	    if (ixEthAccTxFromSwQ(portId,highestPriority)!=IX_SUCCESS)
	    {
                /* nothing left in the sw queue or the hw queues are 
                * full. There is no point to continue to drain the 
                * sw queues
                */
		return;
	    }
	}	
    }  	
    while (--maxQWritesToPerform);
}

/**
 * @brief TxDone event handler
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */

void 
ixEthTxFrameDoneQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{	
    IX_OSAL_MBUF    *mbufPtr;
    UINT32     qEntry;
    UINT32     *qEntryPtr;
    UINT32     txDoneQReadStatus;
    UINT32     portId;

    /*
     * Design note : entries are read in a static buffer, This buffer contains
     * an extra entyry (which is zeroed by the compiler), so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    static UINT32 txDoneQEntry[IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK + 1];

    /* 
     * Indication that Tx frames have been transmitted from the NPE.
     */
    
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.txDoneCallbackCounter);
    
    do{
	qEntryPtr = txDoneQEntry;
	txDoneQReadStatus = ixQMgrQBurstRead(IX_ETH_ACC_TX_DONE_Q, 
		     IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK,
		     qEntryPtr);

#ifndef NDEBUG
	if (txDoneQReadStatus != IX_QMGR_Q_UNDERFLOW
	    && (txDoneQReadStatus != IX_SUCCESS))
	{
	    /*major error*/
	    ixEthAccDataStats.unexpectedError++;
	    IX_ETH_ACC_FATAL_LOG(
		"ixEthTxFrameDoneQMCallback:Error: %u\n", 
		(UINT32)txDoneQReadStatus, 0, 0, 0, 0, 0);
	    return;
	}
#endif

	qEntry = *qEntryPtr;

	while(qEntry != 0)
	{	    
	    mbufPtr = ixEthAccEntryFromQConvert(qEntry,
		      IX_ETHNPE_QM_Q_TXENET_ADDR_MASK);

#ifndef NDEBUG
	    if (mbufPtr == NULL)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthTxFrameDoneQMCallback:Error: Null Mbuf Ptr\n", 
		    0, 0, 0, 0, 0, 0);
		return;
	    }
#endif
	    
	    /* endianness conversions and stats updates */
	    ixEthAccMbufFromTxQ(mbufPtr);

	    /*
	     * Get port id from message.
	     */
	    portId = IX_ETHNPE_NODE_AND_PORT_TO_PHYSICAL_ID(IX_ETHNPE_QM_Q_RXENET_NPEID_VAL(qEntry),IX_ETHNPE_QM_Q_PID_VAL(qEntry));


#ifndef NDEBUG
	    /* Prudent to at least check the port is within range */
	    if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS)
	    {
		ixEthAccDataStats.unexpectedError++;
		IX_ETH_ACC_FATAL_LOG(
		    "ixEthTxFrameDoneQMCallback: Illegal port: %u\n", 
		    (UINT32)portId, 0, 0, 0, 0, 0);
		return;
	    }
#endif

	    TX_STATS_INC(portId,txDoneClientCallback);
            
	    /* 
	     * Call user level callback.
	     */
	    ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn(
		ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag, 
		mbufPtr);

	    /* move to next queue entry */
	    qEntry = *(++qEntryPtr);

	}
    } while( txDoneQReadStatus == IX_SUCCESS );
}

IX_ETH_ACC_PUBLIC
void ixEthAccDataPlaneShow(void)
{
    unsigned numTxQEntries;
    unsigned numTxDoneQEntries;
    UINT32 numRxQEntries;
    unsigned numRxFreeQEntries;
    UINT32 portId;
#ifndef NDEBUG
    UINT32 priority;
    UINT32 numBuffersInRx=0;
    UINT32 numBuffersInTx=0;
    UINT32 numBuffersInSwQ=0;
    UINT32 totalBuffers=0;
    UINT32 rxFreeCallbackCounter = 0;
    UINT32 txCallbackCounter = 0;
#endif
    UINT32 key;

    /* snapshot of stats */
    IxEthAccTxDataStats tx[IX_ETH_ACC_NUMBER_OF_PORTS];
    IxEthAccRxDataStats rx[IX_ETH_ACC_NUMBER_OF_PORTS];
    IxEthAccDataPlaneStats stats;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
	return;
    }

    /* get a reliable snapshot */
    key = ixOsalIrqLock();

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	memcpy(&tx[portId], 
	       &ixEthAccPortData[portId].ixEthAccTxData.stats,
	       sizeof(tx[portId]));
	memcpy(&rx[portId], 
	       &ixEthAccPortData[portId].ixEthAccRxData.stats,
	       sizeof(rx[portId]));
    }
    memcpy(&stats, &ixEthAccDataStats, sizeof(stats));
    
    ixOsalIrqUnlock(key);

#ifdef NDEBUG
    printf("Detailed statistics collection not supported in this load\n");
#endif

    /* print snapshot */
    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	if(IX_ETH_ACC_FAIL == ixEthAccSingleEthNpeCheck(portId))
	{
	    continue ;
	}
 
	printf("PORT %u --------------------------------\n",
	       portId);
#ifndef NDEBUG 
	printf("Tx Done Frames                : %u\n", 
	       tx[portId].txDoneClientCallback + 
	       tx[portId].txDoneSwQDuringDisable + 
	       tx[portId].txDoneDuringDisable);
	printf("Tx Frames                     : %u\n", 
	       tx[portId].txQOK + tx[portId].txQDelayed);
	printf("Tx H/W Q Added OK             : %u\n", 
	       tx[portId].txQOK);
	printf("Tx H/W Q Delayed              : %u\n", 
	       tx[portId].txQDelayed);
	printf("Tx From S/W Q Added OK        : %u\n", 
	       tx[portId].txFromSwQOK);
	printf("Tx From S/W Q Delayed         : %u\n", 
	       tx[portId].txFromSwQDelayed);
	printf("Tx Overflow                   : %u\n", 
	       tx[portId].txOverflow);
	printf("Tx Mutual Lock                : %u\n", 
	       tx[portId].txLock);
	printf("Tx Late Ntf Enabled           : %u\n", 
	       tx[portId].txLateNotificationEnabled);
	printf("Tx Low Thresh CB              : %u\n", 
	       tx[portId].txLowThreshCallback);
	printf("Tx Done from H/W Q (Disable)  : %u\n", 
	       tx[portId].txDoneDuringDisable);
	printf("Tx Done from S/W Q (Disable)  : %u\n", 
	       tx[portId].txDoneSwQDuringDisable);
	for (priority = IX_ETH_ACC_TX_PRIORITY_0;
	     priority <= IX_ETH_ACC_TX_PRIORITY_7;
	     priority++)
	{
	    if (tx[portId].txPriority[priority])
	    {
		printf("Tx Priority %u                 : %u\n", 
		       priority,
		       tx[portId].txPriority[priority]);
	    }
	}
#endif
	printf("Tx unexpected errors          : %u (should be 0)\n", 
	       tx[portId].txUnexpectedError);	
	
#ifndef NDEBUG
	printf("Rx Frames                     : %u\n", 
	       rx[portId].rxFrameClientCallback + 
	       rx[portId].rxSwQDuringDisable+ 
	       rx[portId].rxDuringDisable);
	printf("Rx Free Replenish             : %u\n", 
	       rx[portId].rxFreeRepOK + rx[portId].rxFreeRepDelayed);
	printf("Rx Free H/W Q Added OK        : %u\n", 
	       rx[portId].rxFreeRepOK);
	printf("Rx Free H/W Q Delayed         : %u\n", 
	       rx[portId].rxFreeRepDelayed);
	printf("Rx Free From S/W Q Added OK   : %u\n", 
	       rx[portId].rxFreeRepFromSwQOK);
	printf("Rx Free From S/W Q Delayed    : %u\n", 
	       rx[portId].rxFreeRepFromSwQDelayed);
	printf("Rx Free Overflow              : %u\n", 
	       rx[portId].rxFreeOverflow);
	printf("Rx Free Mutual Lock           : %u\n", 
	       rx[portId].rxFreeLock);
	printf("Rx Free Late Ntf Enabled      : %u\n", 
	       rx[portId].rxFreeLateNotificationEnabled);
	printf("Rx Free Low CB                : %u\n",
	       rx[portId].rxFreeLowCallback);
	printf("Rx From H/W Q (Disable)       : %u\n", 
	       rx[portId].rxDuringDisable);
	printf("Rx From S/W Q (Disable)       : %u\n", 
	       rx[portId].rxSwQDuringDisable);
	printf("Rx unlearned Mac Address      : %u\n", 
	       rx[portId].rxUnlearnedMacAddress);
        printf("Rx Filtered (Rx => RxFree)    : %u\n",
            rx[portId].rxFiltered);

	for (priority = IX_ETH_ACC_TX_PRIORITY_0;
	     priority <= IX_ETH_ACC_TX_PRIORITY_7;
	     priority++)
	{
	    if (rx[portId].rxPriority[priority])
	    {
		printf("Rx Priority %u                 : %u\n", 
		       priority,
		       rx[portId].rxPriority[priority]);
	    }
	}
#endif
	printf("Rx unexpected errors          : %u (should be 0)\n", 
	       rx[portId].rxUnexpectedError);	
	
#ifndef NDEBUG
	numBuffersInTx = tx[portId].txQOK + 
	    tx[portId].txQDelayed -
	    tx[portId].txDoneClientCallback - 
	    tx[portId].txDoneSwQDuringDisable - 
	    tx[portId].txDoneDuringDisable;

	printf("# Tx Buffers currently for transmission : %u\n", 
	       numBuffersInTx);
	
	numBuffersInRx = rx[portId].rxFreeRepOK + 
	    rx[portId].rxFreeRepDelayed - 
	    rx[portId].rxFrameClientCallback - 
	    rx[portId].rxSwQDuringDisable - 
	    rx[portId].rxDuringDisable;

	printf("# Rx Buffers currently for reception    : %u\n", 
	       numBuffersInRx);	

	totalBuffers += numBuffersInRx + numBuffersInTx;
#endif
    }
    
    printf("---------------------------------------\n");

#ifndef NDEBUG
    printf("\n");
    printf("Mbufs :\n");   
    printf("Tx Unchained mbufs            : %u\n", 
	   stats.unchainedTxMBufs);
    printf("Tx Chained bufs               : %u\n", 
	   stats.chainedTxMBufs);
    printf("TxDone Unchained mbufs        : %u\n", 
	   stats.unchainedTxDoneMBufs);
    printf("TxDone Chained bufs           : %u\n", 
	   stats.chainedTxDoneMBufs);
    printf("RxFree Unchained mbufs        : %u\n", 
	   stats.unchainedRxFreeMBufs);
    printf("RxFree Chained bufs           : %u\n", 
	   stats.chainedRxFreeMBufs);
    printf("Rx Unchained mbufs            : %u\n", 
	   stats.unchainedRxMBufs);
    printf("Rx Chained bufs               : %u\n", 
	   stats.chainedRxMBufs);

    printf("\n");
    printf("Software queue usage :\n");   
    printf("Buffers added to S/W Q        : %u\n", 
	   stats.addToSwQ);
    printf("Buffers removed from S/W Q    : %u\n", 
	   stats.removeFromSwQ);

    printf("\n");
    printf("Hardware queues callbacks :\n");   

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	rxFreeCallbackCounter += rx[portId].rxFreeLowCallback;
	txCallbackCounter += tx[portId].txLowThreshCallback;
    }
    printf("Tx Done QM Callback invoked   : %u\n", 
	   stats.txDoneCallbackCounter);
    printf("Tx QM Callback invoked        : %u\n", 
	   txCallbackCounter);
    printf("Rx QM Callback invoked        : %u\n", 
	   stats.rxCallbackCounter);
    printf("Rx QM Callback burst read     : %u\n", 
	   stats.rxCallbackBurstRead);
    printf("Rx Free QM Callback invoked   : %u\n", 
	   rxFreeCallbackCounter);
#endif
    printf("Unexpected errors in CB       : %u (should be 0)\n", 
	   stats.unexpectedError);
    printf("\n");

    printf("Hardware queues levels :\n");   

    numRxQEntries = 0;
    ixEthAccQMgrRxQEntryGet(&numRxQEntries);
    printf("Receive Queue(s)              : %u \n",numRxQEntries);

    numTxDoneQEntries = 0;
    ixQMgrQNumEntriesGet( IX_ETH_ACC_TX_DONE_Q, &numTxDoneQEntries);
    printf("Transmit Done Queue           : %u \n",numTxDoneQEntries);

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	/* only show queue info for supported ports */
        if(IX_ETH_ACC_FAIL == ixEthAccSingleEthNpeCheck(portId))
        {
            continue ;
        }
	numTxQEntries = 0;
	ixQMgrQNumEntriesGet(IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), &numTxQEntries);
	printf("Transmit Port %u Queue         : %u \n",portId,numTxQEntries);

	numRxFreeQEntries = 0;
	ixQMgrQNumEntriesGet(IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId), &numRxFreeQEntries);
	printf("Receive Free Port %u Queue     : %u \n",portId,numRxFreeQEntries);
    }

#ifndef NDEBUG
    printf("\n");
    printf("# Total Buffers accounted for : %u\n", 
	   totalBuffers);

    numBuffersInSwQ = ixEthAccDataStats.addToSwQ - 
	ixEthAccDataStats.removeFromSwQ;

    printf("    Buffers in S/W Qs         : %u\n", 
	   numBuffersInSwQ);
    printf("    Buffers in H/W Qs or NPEs : %u\n", 
	   totalBuffers - numBuffersInSwQ);
#endif

    printf("Rx QoS Discipline             : %s\n",
	   (ixEthAccDataInfo.schDiscipline == 
	    FIFO_PRIORITY ) ? "Enabled" : "Disabled");

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
	printf("Tx QoS Discipline port %u      : %s\n", 
	       portId,
	       (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline == 
		FIFO_PRIORITY ) ? "Enabled" : "Disabled"); 
    }
    printf("\n");
}


